package cn.wgx.modules.base.services;

import cn.wgx.commons.util.*;
import cn.wgx.modules.base.entity.BaseEntity;
import cn.wgx.modules.base.entity.mapper.BaseUtilMapper;
import cn.wgx.modules.sys.entity.SysUser;
import cn.wgx.modules.sys.util.UserUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import io.swagger.models.auth.In;
import jdk.nashorn.internal.runtime.regexp.joni.Regex;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.*;
import java.util.*;


public abstract class BaseService<T extends BaseEntity> extends BaseLogger {


    public static final String cacheName = "sysCache";

    protected static final List emptyList = new ArrayList();

    protected Object emptyObject = new Object();

    protected BaseEntity baseEntity;

    //可执行自定义SQL的mapper
    @Autowired
    BaseUtilMapper baseUtilMapper;

    public int updateClick(int id, String tableName, String fieldName, String idName) {
        if (tableName == null || fieldName == null || idName == null) {
            return 0;
        }
        return baseUtilMapper.plusOne(id, tableName, fieldName, idName);
    }

    public String getCachePreKey() {
        return "CACHE_" + this.getClass().getName() + "_";
    }

    // addCustomSQL(criteria, " t.pass%2 = ", "1");
    protected int addCustomSQL(Object criteriaObj, String condition) {
        return addCustomSQL(criteriaObj, condition + " and 1=", "1");
    }

    // addCustomSQL(criteria, " t.pass%2 = ", "1");
    protected int addCustomSQL(Object criteriaObj, String condition, String value) {
        if (!validateSQL(condition)) {
            //验证sql是否有注入字段
            return 0;
        }
        Map<String, Object> param = new HashMap<>();
        param.put(condition, value);
        return doAddParam(criteriaObj, param);
    }

    protected boolean validateSQL(String sql) {
        //todo 完整验证sql注入
        if (StringUtils.isNotBlank(sql) && containError(sql)) {
            return true;
        }
        return false;
    }

    private static boolean containError(String source) {
        source = source.toLowerCase();
        if (source.contains("--") ||
                source.contains("insert")
                || source.contains("update")
                || source.contains("delete")
                || source.contains("drop")
                || source.contains("truncate")
                || source.contains("declare")
                || source.contains("xp_cmdshell")
                || source.contains("/add")
                || source.contains("net user")
                || source.contains("exec")
                || source.contains("execute")
                || source.contains("xp_")
                || source.contains("sp_")
                || source.contains("0x")
        ) {
            return false;
        }
        return true;
    }

    /**
     * 反射加公共sql参数,确保包含字段createDate
     * 1. 时间范围
     *
     * @param t
     * @param criteriaObj
     */
    protected <E extends BaseEntity> void addPublicCondition(E t, Object criteriaObj) {
        Map<String, Object> param = new HashMap<>();
        if (t.get_sDate() != null) {
            param.put(" t.createDate >= ", t.get_sDate());
        }
        if (t.get_eDate() != null) {
            param.put(" t.createDate < ", DateUtils.getNextDateZero(t.get_eDate()));
        }
        if (!param.isEmpty()) {
            doAddParam(criteriaObj, param);
        }
    }

    private int doAddParam(Object criteriaObj, Map<String, Object> param) {
        Class<?> aClass = criteriaObj.getClass();
        int n = 0;
        try {
            Field field = ReflectionUtils.getField(aClass, "criteria");
            if (field != null) {
                //判断field类型
                Class<?> type = field.getType();
                Type genericType = field.getGenericType();
                //判断是否是泛型类
                if (type.equals(List.class)
                        && genericType instanceof ParameterizedType) {
                    field.setAccessible(true);
                    List arrayList = (List) field.get(criteriaObj);
                    Class<?> actualTypeArguments = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
                    Constructor<?> constructor = actualTypeArguments.getDeclaredConstructor(String.class, Object.class);
                    constructor.setAccessible(true);
                    for (Map.Entry entry : param.entrySet()) {
                        Object o = constructor.newInstance(entry.getKey(), entry.getValue());
                        arrayList.add(o);
                        n++;
                    }
                }
            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return n;
    }

    abstract public List<T> getListByIds(List<Integer> ids);

    protected List<Integer> getUniList(List<Integer> ids) {
        if (ids != null) {
            Set<Integer> set = Sets.newHashSet(ids.size());
            set.addAll(ids);
            ids.clear();
            ids.addAll(set);
            return ids;
        }
        return Lists.newArrayList();
    }

    /**
     * 填充DTO list的引用对象
     *
     * @param list
     * @param idFieldName     引用对象的ID, user_id,createUser_id
     * @param targetFieldName 引用对象的field名称, sysUser
     * @param tIdFieldName    原始对象的ID名, user_id
     * @param <E>
     * @return 返回找到的数量
     */
    public <E> int wrapListByIds(List<E> list, String idFieldName, String targetFieldName, String tIdFieldName) {
        //todo 批量包装
        if (list != null && !list.isEmpty()) {
            Class<?> aClass = list.get(0).getClass();
            Class<T> entityClass = getTClass();
            Field targetField = ReflectionUtils.getField(aClass, targetFieldName);

            Field idField = ReflectionUtils.getField(aClass, idFieldName);
            Field sourceIdField = ReflectionUtils.getField(entityClass, tIdFieldName);

            if (targetField == null || idField == null || sourceIdField == null) {
                return 0;
            }
            Class<?> targetType = targetField.getType();
            if (entityClass.isAssignableFrom(targetType)) {
                List<Integer> integerIdList = getIntegerIdList(list, idFieldName);//speciality_id
                if (integerIdList != null && !integerIdList.isEmpty()) {
                    List<T> list2 = null;
                    for (int i = 0, seed = 1000, n = integerIdList.size(); i < (n % seed == 0 ? n / seed : n / seed + 1); i++) {
                        int min = Math.min(i * seed + seed, n);
                        if (list2 == null) {
                            list2 = getListByIds(integerIdList.subList(i * seed, min));
                        } else {
                            list2.addAll(getListByIds(integerIdList.subList(i * seed, min)));
                        }
                    }
//                    List<T> listByIds = getListByIds(integerIdList);
                    if (list2 != null && !list2.isEmpty()) {
                        return wrapListWithResList(list, idFieldName, targetFieldName, list2, tIdFieldName);
                    }
                }
            }

        }
        return 0;
    }


    //填充对象
    public <E,F>  int wrapListWithResList(List<E> list, String idFieldName, String targetFieldName, List<F> list2, String tIdFieldName) {
        //todo 批量包装
        if (list != null && !list.isEmpty() && list2 != null && !list2.isEmpty()) {
            Class<?> aClass = list.get(0).getClass();
            Class<?> entityClass = list2.get(0).getClass();
            Field targetField = ReflectionUtils.getField(aClass, targetFieldName);

            Field idField = ReflectionUtils.getField(aClass, idFieldName);
            Field sourceIdField = ReflectionUtils.getField(entityClass, tIdFieldName);

            if (targetField == null || idField == null || sourceIdField == null) {
                return 0;
            }
            Class<?> targetType = targetField.getType();
            if (entityClass.isAssignableFrom(targetType)) {
                try {
                    idField.setAccessible(true);
                    targetField.setAccessible(true);
                    sourceIdField.setAccessible(true);
                    int r = 0;
                    for (E e : list) {
                        Object o = idField.get(e);
                        if (o instanceof Integer) {
                            for (F t : list2) {
                                Object o1 = sourceIdField.get(t);
                                if (o1 instanceof Integer && o1.equals(o)) {
                                    targetField.set(e, t);
                                    r++;
                                    break;
                                }
                            }
                        }

//                            targetField.
                    }
                    return r;

                } catch (IllegalAccessException ex) {
                    ex.printStackTrace();
                }


            }

        }
        return 0;
    }


    /**
     * 通过反射获取 对象指定field list
     *
     * @param list
     * @param fieldName
     * @param <E>
     * @return
     */
    public <E> List<Integer> getIntegerIdList(List<E> list, String fieldName) {
        ArrayList<Integer> arrayList = Lists.newArrayList();
        if (list != null && !list.isEmpty()) {
            Field field1 = ReflectionUtils.getField(list.get(0).getClass(), fieldName);
            if (field1 == null) {
                return arrayList;
            }
            try {
                field1.setAccessible(true);
                for (E o : list) {
                    Object o1 = field1.get(o);
                    if (o1 instanceof Integer) {
                        arrayList.add((Integer) o1);
                    } else {
                        break;
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        if (!arrayList.isEmpty()) {
            Set set = Sets.newHashSet();
            set.addAll(arrayList);
            arrayList.clear();
            arrayList.addAll(set);
        }
        return arrayList;
    }


    /**
     * 过滤参数
     *
     * @param t
     * @param isInsert
     */
    protected void filter(T t, boolean isInsert) {
        if (t == null) return;
        SysUser user = UserUtil.getUser();
        Integer nowUserId = -1;
        Date nowDate = new Date();
        if (user != null) {
            nowUserId = user.getUser_id();
        }
        if (isInsert) {
            if (Validation.isNullOrZero(t.getCreateUserId())) {
                t.setCreateUserId(nowUserId);
            }
            t.setCreateDate(nowDate);
        } else {
            t.setCreateUserId(null);
            t.setCreateDate(null);
            t.setUpdate(true);
        }
        t.setUpdateUserId(nowUserId);
        t.setUpdateDate(nowDate);
        t.setInsert(isInsert);
    }


    protected T wrap(T t) {
        return t;
    }


    @Transactional(readOnly = true)
    public  List wrap(List<T> list) {
        if (null != list && !list.isEmpty()) {
            ArrayList arrayList = new ArrayList(list.size());
            for (T t : list) {
                T wrap = wrap(t);
                if (null != wrap) {
                    arrayList.add(wrap);
                }
            }
            return arrayList;
        } else
            return emptyList;
    }

    public Class<T> getTClass() {
        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
//
//    public abstract Object getObjectById(int id);
//
//    public abstract List<Object> getObjectList(int l, int n);
//
//    public abstract List<Object> getObjectListByKeyWord(String vedioKeyWord, int l, int n);
//
//    public abstract int addObject(Object object);
//
//    public abstract int delObjectById(int id);
//
//    public abstract int modifyObject(Object object);
//
//    public abstract int updateClick(int id);
}
